//
//  wl_base64.cpp
//  pushservice
//
//  Created by yunfan on 14/12/18.
//  Copyright (c) 2014年 yunfan. All rights reserved.
//

#include <string.h>
#include <stdint.h>
// #include <ctype.h>

static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const char reverse_table[128] = {
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64
};

int wl_base64_encode_output_size(uint32_t inputSize) {
    return (((inputSize + 2) / 3) * 4);
}

int wl_base64_encode(const char *pInput, uint32_t inputSize, char *pOutput, uint32_t* pOutputSize)
{
    int bits_collected = 0;
    int i;
    uint32_t accumulator = 0;
    uint32_t encodedStrSize = (((inputSize + 2) / 3) * 4);
    uint32_t max_int = 0xffffffff - 10;
    uint32_t outputPos = 0;

    if (inputSize > max_int) {
        return -1;
    }

    if (*pOutputSize < encodedStrSize) {
        return -1;
    }

    // Use = signs so the end is properly padded.
    memset(pOutput, '=', encodedStrSize);
    
    for (i = 0; i < inputSize; i++) {
        accumulator = (accumulator << 8) | (pInput[i] & 0xffu);
        bits_collected += 8;
        while (bits_collected >= 6) {
            bits_collected -= 6;
            pOutput[outputPos++] = b64_table[(accumulator >> bits_collected) & 0x3fu];
        }
    }

    if (bits_collected > 0) { // Any trailing bits that are missing.
        if (bits_collected < 6) {
            accumulator <<= (6 - bits_collected);
            pOutput[outputPos++] = b64_table[accumulator & 0x3fu];
        }else {
            return -1;
        }
    }

    if (outputPos < (encodedStrSize - 2) || outputPos > encodedStrSize) {
        return -1;
    }

    *pOutputSize = outputPos;

    return 0;
}

int wl_base64_decode(const char *pInput, uint32_t inputSize, char *pOutput, uint32_t *pOutputSize)
{
    int bits_collected = 0;
    int i;

    uint32_t accumulator = 0;
    uint32_t output = 0;

    for (i = 0; i < inputSize; i++) {
        const char c = pInput[i];
        if (/*isspace(c)*/ c == 0x20 || c == '=') {
            // Skip whitespace and padding. Be liberal in what you accept.
            continue;
        }

        if ((c > 127) || (c < 0) || (reverse_table[(int)c] > 63)) {
            return -1;
        }

        accumulator = (accumulator << 6) | reverse_table[(int)c];
        bits_collected += 6;
        if (bits_collected >= 8) {
            bits_collected -= 8;
            pOutput[output++] = (char)((accumulator >> bits_collected) & 0xffu);
        }

        if (output > *pOutputSize) {
            return -1;
        }
    }

    *pOutputSize = output;

    return 0;
}
